package com.qf.base.utils.smsg;

import com.mchange.v2.lang.ObjectUtils;
import com.qf.base.result.R;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.crypto.tls.PRFAlgorithm;
import org.springframework.util.CollectionUtils;

import java.io.UnsupportedEncodingException;
import java.time.LocalDate;
import java.util.*;

@Slf4j
public class SMS4 {
    private static final  SM4Utils MYSM4=new SM4Utils();

    public static final  int ENCRYPT=1;
    public static final  int DECRYPT=0;
    public static final  int ROUND=32;
    public static final int BLOCK=16;
    public static  byte[] SYEK={
            0x01,0x23,0x45,0x67,(byte) 0x89,(byte) 0xab,(byte) 0xcd,(byte) 0xef,(byte) 0xfe,(byte) 0xdc,(byte) 0xba,(byte) 0x98,0x76,0x54,0x32,0x10
    };
    private static byte[] sbox={(byte) 0xd6,(byte) 0x90,(byte) 0xe9,(byte) 0xfe,(byte) 0xcc,(byte) 0xe1,0x3d,(byte) 0xb7,0x16,(byte) 0xb6,0x14,(byte) 0xc2,0x28,(byte) 0xfb,0x2c,0x05,0x2b,0x67,(byte) 0x9a,0x76,0x2a,(byte) 0xbe,0x04,(byte) 0xc3,(byte) 0xaa,0x44,0x13,0x26,0x49,(byte) 0x86,0x06,(byte) 0x99,(byte) 0x9c,0x42,0x50,(byte) 0xf4,(byte) 0x91,(byte) 0xef,(byte) 0x98,0x7a,0x33,0x54,0x0b,0x43,(byte) 0xed,(byte) 0xcf,(byte) 0xac,0x62,(byte) 0xe4,(byte) 0xb3,0x1c,(byte) 0xa9,(byte) 0xc9,0x08,(byte) 0xe8,(byte) 0x95,(byte) 0x80,(byte) 0xdf,(byte) 0x94,(byte) 0xfa,(byte) 0x75,(byte) 0x8f,0x3f,(byte) 0xa6,0x47,0x07,(byte) 0xa7,(byte) 0xfc,(byte) 0xf3,0x73,0x17,(byte) 0xba,(byte) 0x83,0x59,0x3c,0x19,(byte) 0xe6,(byte) 0x85,0x4f,(byte) 0xa8,0x68,0x6b,(byte) 0x81,(byte) 0xb2,0x71,0x64,(byte) 0xda,(byte) 0x8b,(byte) 0xf8,(byte) 0xeb,0x0f,0x4b,0x70,0x56,(byte) 0x9d,0x35,0x1e,0x24,0x0e,0x5e,0x63,0x58,(byte) 0xd1,(byte) 0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,(byte) 0x87,(byte) 0xd4,0x00,0x46,0x57,(byte) 0x9f,(byte) 0xd3,0x27,0x52,0x4c,0x36,0x02,(byte) 0xe7,(byte) 0xa0,(byte) 0xc4,(byte) 0xc8,(byte) 0x9e,(byte) 0xea,(byte) 0xbf,(byte) 0x8a,(byte) 0xd2,0x40,(byte) 0xc7,0x38,(byte) 0xb5,(byte) 0xa3,(byte) 0xf7,(byte) 0xf2,(byte) 0xce,(byte) 0xf9,0x61,0x15,(byte) 0xa1,(byte) 0xe0,(byte) 0xae,0x5d,(byte) 0xa4,(byte) 0x9b,0x34,0x1a,0x55,(byte) 0xad,(byte) 0x93,0x32,0x30,(byte) 0xf5,(byte) 0x8c,(byte) 0xb1,(byte) 0xe3,0x1d,(byte) 0xf6,(byte) 0xe2,0x2e,(byte) 0x82,0x66,(byte) 0xca,0x60,(byte) 0xc0,0x29,0x23,(byte) 0xab,0x0d,0x53,0x4e,0x6f,(byte) 0xd5,(byte) 0xdb,0x37,0x45,(byte) 0xde,(byte) 0xfd,(byte) 0x8e,0x2f,0x03,(byte) 0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,(byte) 0x8d,0x1b,(byte) 0xaf,(byte) 0x92,(byte) 0xbb,(byte) 0xdd,(byte) 0xbc,0x7f,0x11,(byte) 0xd9,0x5c,0x41,0x1f,0x10,0x5a,(byte) 0xd8,0x0a,(byte) 0xc1,0x31,(byte) 0x88,(byte) 0xa5,(byte) 0xcd,0x7b,(byte) 0xbd,0x2d,0x74,(byte) 0xd0,0x12,(byte) 0xb8,(byte) 0xe5,(byte) 0xb4,(byte) 0xb0,(byte) 0x89,0x69,(byte) 0x97,0x4a,0x0c,(byte) 0x96,0x77,0x7e,0x65,(byte) 0xb9,(byte) 0xf1,0x09,(byte) 0xc5,0x6e,(byte) 0xc6,(byte) 0x84,0x18,(byte) 0xf0,0x7d,(byte) 0xec,0x3a,(byte) 0xdc,0x4d,0x20,0x79,(byte) 0xee,0x5f,0x3e,(byte) 0xd7,(byte) 0xcb,0x39,0x48};

    private static int[] CK={0x00070e15,0x1c232a31,0x383f464d,0x545b6269,0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9,0xe0e7eef5,0xfc030a11,0x181f262d,0x343b4249,0x50575e65,0x6c737a81,0x888f969d,0xa4abb2b9,0xc0c7ced5,0xdce3eaf1,0xf8ff060d,0x141b2229,0x30373e45,0x4c535a61,0x686f767d,0x848b9299,0xa0a7aeb5,0xbcc3cad1,0xd8dfe6ed,0xf4fb0209,0x10171e25,0x2c333a41,0x484f565d,0x646b7279};

    private static int Rotl(int x,int y){
        return x<<y|x>>>(32-y);
    }
    private static int ByteSub(int A){
        return (sbox[A >>>24 & 0xFF]&0xFF)<<24|(sbox[A>>>16&0xFF]&0xFF)<<16|(sbox[A>>>8&0xFF]&0xFF)<<8|(sbox[A&0xFF]&0xFF);
    }
    private static int L1(int B){
        return B^Rotl(B,2)^Rotl(B,10)^Rotl(B,18)^Rotl(B,24);
    }
    private static int L2(int B){
        return B^Rotl(B,13)^Rotl(B,23);
    }
    private static void SMS4Crypt(byte[] Input,byte[] Output,int[]rk){
        int r,mid,x0,x1,x2,x3;
        int[] x=new int[4];
        int[] tmp=new int[4];
        for (int i=0;i<4;i++){
            tmp[0]=Input[0+4*i]&0xff;
            tmp[1]=Input[1+4*i]&0xff;
            tmp[2]=Input[2+4*i]&0xff;
            tmp[3]=Input[3+4*i]&0xff;
            x[i]=tmp[0]<<24|tmp[1]<<16|tmp[2]<<8|tmp[3];
        }
        for (r = 0; r<32 ; r+=4) {
            mid=x[1]^x[2]^x[3]^rk[r+0];
            mid=ByteSub(mid);
            x[0]=x[0]^L1(mid);

            mid=x[2]^x[3]^x[0]^rk[r+1];
            mid=ByteSub(mid);
            x[1]=x[1]^L1(mid);

            mid=x[3]^x[0]^x[1]^rk[r+2];
            mid=ByteSub(mid);
            x[2]=x[2]^L1(mid);

            mid=x[0]^x[1]^x[2]^rk[r+3];
            mid=ByteSub(mid);
            x[3]=x[3]^L1(mid);
        }
        for (int j = 0; j <16 ; j+=4) {
            Output[j]=(byte) (x[3-j/4]>>>24&0xFF);
            Output[j+1]=(byte) (x[3-j/4]>>>16&0xFF);
            Output[j+2]=(byte) (x[3-j/4]>>>8&0xFF);
            Output[j+3]=(byte) (x[3-j/4]&0xFF);
        }
    }
    private static void SMS4KeyExt(byte[] Key,int[] rk,int CryptFlag){
        int r,mid;
        int[] x=new int[4];
        int[] tmp=new int[4];
        for (int i=0;i<4;i++){
            tmp[0]=Key[0+4*i]&0xff;
            tmp[1]=Key[1+4*i]&0xff;
            tmp[2]=Key[2+4*i]&0xff;
            tmp[3]=Key[3+4*i]&0xff;
            x[i]=tmp[0]<<24|tmp[1]<<16|tmp[2]<<8|tmp[3];
        }
        x[0]^=0xa3b1bac6;
        x[1]^=0x56aa3350;
        x[2]^=0x677d9197;
        x[3]^=0xb27022dc;
        for (r = 0; r<32 ; r+=4) {
            mid=x[1]^x[2]^x[3]^CK[r+0];
            mid=ByteSub(mid);
            rk[r+0]=x[0]^=L2(mid);

            mid=x[2]^x[3]^x[0]^CK[r+1];
            mid=ByteSub(mid);
            rk[r+1]=x[1]^=L2(mid);

            mid=x[3]^x[0]^x[1]^CK[r+2];
            mid=ByteSub(mid);
            rk[r+2]=x[2]^=L2(mid);

            mid=x[0]^x[1]^x[2]^CK[r+3];
            mid=ByteSub(mid);
            rk[r+3]=x[3]^=L2(mid);
        }
        //解密时轮密钥使用顺序：rk31,rk30,...,rk30
        if (CryptFlag ==DECRYPT){
            for ( r = 0; r < 16; r++) {
                mid=rk[r];
                rk[r]=rk[31-r];
                rk[31-r]=mid;
            }
        }

    }
    //加密解密函数
    private static int sms4(byte[] in,int inLen,byte[]key,byte[]out,int CryptFlag){
        int point=0;
        int[] round_key=new int[ROUND];
        SMS4KeyExt(key,round_key,CryptFlag);
        byte[] input=new byte[16];
        byte[] output=new byte[16];
        while (inLen >=BLOCK){
            input= Arrays.copyOfRange(in,point,point+16);
            SMS4Crypt(input,output,round_key);
            System.arraycopy(output,0,out,point,BLOCK);
            inLen-=BLOCK;
            point+=BLOCK;
        }
        return 0;
    }
    //对字符串加密
    public static byte[] encryptStr(String inStr){
        if (inStr == null || inStr.equals("")){
            return null;
        }
        return encryptBytes(inStr.getBytes());
    }
    //对字符串解密
    public static String decryptStr(String hexStr){
        byte[] in =SMS4.hexStringToByte(hexStr);
        byte[] out=decryptBytes(in);
        String result = new String(out).trim();
        return result;
    }
    //对二进制流加密
    public static byte[] encryptBytes(byte[] inBytes){
        if (inBytes == null || inBytes.length==0) {
            return null;
        }
        int len=inBytes.length;
        len +=16-(len%16);
        byte[] out=new byte[len];
        sms4(inBytes, len,SMS4.SYEK, out,SMS4.ENCRYPT);
        return out;
    }
    //对二进制流解密
    public static byte[] decryptBytes(byte[] in){
        byte[] out=new byte[in.length];
        SMS4.sms4(in,in.length,SMS4.SYEK,out,SMS4.DECRYPT);
        return out;
    }
    /**
     * 字符串转换乘十六禁止字符串
     */
    public static String str2HexStx(String str){
        char[] chars="0123456789ABCDEF".toCharArray();
        StringBuilder sb=new StringBuilder("");
        byte[] bs=str.getBytes();
        int bit;
        for (int i = 0; i < bs.length; i++) {
            bit = (bs[i]& 0x0f0)>>4;
            sb.append(chars[bit]);
            bit=bs[i]&0x0f;
            sb.append(chars[bit]);
        }
        return sb.toString();
    }
    /**
     * 把十六进制字符串转换为字节数组
     */
    public static byte[] hexStringToByte(String hex){
        int len=(hex.length()/2);
        byte[] result=new byte[len];
        char[] achar=hex.toCharArray();
        for (int i = 0; i < len; i++) {
            int pos=i*2;
            result[i]=(byte) (toByte(achar[pos])<<4|toByte(achar[pos+1]));
        }
        return result;
    }
    private static  int toByte(char c){
        byte b=(byte) "0123456789ABCDEF".indexOf(c);
        return b;
    }
    /**
     * 数组转换成十六进制字符串
     */
    public static final String bytesToHexString(byte[] bArray){
        StringBuffer sb=new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sTemp=Integer.toHexString(0xFF&bArray[i]);
            if (sTemp.length()<2){
                sb.append(0);
            }
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }
    /**
     * 十六禁止字符串转换成字符串
     */
    public static String hexStr2Str(String hexStr){
        String str="0123456789ABCDEF";
        char[] hexs=hexStr.toCharArray();
        byte[] bytes=new byte[hexStr.length()/2];
        int n;
        for (int i = 0; i < bytes.length; i++) {
            n=str.indexOf(hexs[2*i]*16);
            n+=str.indexOf(hexs[2*i+1]);
            bytes[i]=(byte) (n&0xff);
        }
        return new String(bytes);
    }
    /**
     * 数据解密
     */
    public static synchronized String SG_DecECBData(String encData,String secret){
        byte[] secretBytes=secret.getBytes();
        byte[] encDataBytes=hexStringToByte(encData);
        long star=System.currentTimeMillis();
        try {
            String decData=new String(MYSM4.SG_DecECBDate(secretBytes,encDataBytes),"UTF-8");
            log.info("数据解密耗时：{}秒",(System.currentTimeMillis()-star+0.0)/1000);
            System.out.println("获取到解密数据 ："+decData);
            return decData;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 数据加密
     */
    public static synchronized String SG_EncECBData(String decData,String secret){
        byte[] secretBytes=secret.getBytes();
        byte[] encDataBytes=decData.getBytes();
        long star=System.currentTimeMillis();
        String encData=bytesToHexString(MYSM4.SG_EncECBData(secretBytes,encDataBytes));
        log.info("数据加密耗时：{}秒",(System.currentTimeMillis()-star+0.0)/1000);
        System.out.println("获取到解密数据： "+decData);
        return encData;
    }

    public static void main(String[] args) {
//        //数据解密
        String s=decryptStr("DDB1101B24AA8DF375877F9322705018").toUpperCase();
        System.out.println(s);
        //数据加密
        System.out.println(bytesToHexString(encryptStr("JDBC:ORCALE:thin:@27.196.200.154:11521/fjpms")));
    }
}
